package com.jiyongjun.component.utils;

import com.jiyongjun.component.annotation.ApiFilterIgnore;
import com.jiyongjun.component.annotation.ApiMethodMetadata;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.rpc.Invocation;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <h2></h2>
 * <h3>描述：</h3>
 *
 * @author yongjun.jyj
 * @since 2022/7/14 17:56
 */
public class LogContextUtils {
    private static final Map<String, ApiMethodMetadata> API_METHOD_METADATA_CACHE_MAP = new ConcurrentHashMap<>();
    private static final Map<String, Boolean> API_FILTER_IGNORE_CACHE_MAP = new ConcurrentHashMap<>();

    @ApiMethodMetadata(desc = "")
    private void emptyMethod() {}

    private static ApiMethodMetadata defaultApiMethodMetadata;
    static {
        try {
            Method emptyMethod = LogContextUtils.class.getDeclaredMethod("emptyMethod");
            emptyMethod.setAccessible(true);
            defaultApiMethodMetadata = emptyMethod.getAnnotation(ApiMethodMetadata.class);
        } catch (NoSuchMethodException e) {
            // ignore
        }
    }

    public static ApiMethodMetadata getApiMethodMetadata(Invocation invocation) {
        String apiUniqueName = generateServerApiUniqueName(invocation);
        return API_METHOD_METADATA_CACHE_MAP.computeIfAbsent(apiUniqueName, key -> {
            Method method = getMethod(invocation);

            // 1.从实现类方法上取
            try {
                Method implMethod = invocation.getServiceModel().getProxyObject().getClass()
                        .getMethod(invocation.getMethodName(), invocation.getParameterTypes());
                if (implMethod.isAnnotationPresent(ApiMethodMetadata.class)) {
                    return implMethod.getAnnotation(ApiMethodMetadata.class);
                }
            } catch (NoSuchMethodException e) {
                // ignore
            }
            // 2.从接口方法上取
            if (method.isAnnotationPresent(ApiMethodMetadata.class)) {
                return method.getAnnotation(ApiMethodMetadata.class);
            }

            // 返回默认值
            return defaultApiMethodMetadata;
        });
    }

    public static boolean isAnnotatedApiFilterIgnore(Invocation invocation) {
        String apiUniqueName = generateServerApiUniqueName(invocation);
        return API_FILTER_IGNORE_CACHE_MAP.computeIfAbsent(apiUniqueName, key -> {
            Method method = getMethod(invocation);
            if (method.isAnnotationPresent(ApiFilterIgnore.class) || method.getDeclaringClass().isAnnotationPresent(ApiFilterIgnore.class)) {
                return true;
            }

            // 从实现类方法上取
            Class<?> serviceClass = invocation.getServiceModel().getProxyObject().getClass();
            if (serviceClass.isAnnotationPresent(ApiFilterIgnore.class)) {
                return true;
            }

            try {
                Method implMethod = serviceClass.getMethod(method.getName(), method.getParameterTypes());
                if (implMethod.isAnnotationPresent(ApiFilterIgnore.class)) {
                    return true;
                }
            } catch (NoSuchMethodException e) {
                // ignore
            }
            return false;
        });
    }

    private static Method getMethod(Invocation invocation) {
        return invocation.getServiceModel().getServiceModel().getMethod(invocation.getMethodName(), invocation.getParameterTypes()).getMethod();
    }

    public static String generateServerApiUniqueName(Invocation invocation) {
        return generateMethodUniqueName(getMethod(invocation));
    }

    public static String generateMethodUniqueName(Method method) {
        StringBuilder name = new StringBuilder(method.getDeclaringClass().getName())
                .append("#").append(method.getName()).append("(");
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        if (genericParameterTypes.length > 0) {
            for (Type parameterType : genericParameterTypes) {
                name.append(parameterType.getTypeName()).append(",");
            }

            name.deleteCharAt(name.length() - 1);
        }

        name.append(")");

        return name.toString();
    }

    public static String generateServerApiName(Invocation invocation) {
        ApiMethodMetadata methodMetadata = getApiMethodMetadata(invocation);
        if (methodMetadata != null && StringUtils.isNotBlank(methodMetadata.apiName())) {
            return methodMetadata.apiName();
        }

        // todo 判断其他渠道接口请求

        return generateMethodSimpleName(getMethod(invocation));
    }
    public static String generateMethodSimpleName(Method method) {
        return method.getDeclaringClass().getSimpleName() + "#" + method.getName();
    }
}
